require 'fastlane'
require 'fastlane_core'
require 'spaceship'
require 'terminal-table'
require 'precheck/rule_processor'

module Precheck
  class Runner
    attr_accessor :spaceship

    # Uses the spaceship to download app metadata and then run all rule checkers
    def run
      Precheck.config.load_configuration_file(Precheck.precheckfile_name)

      FastlaneCore::PrintTable.print_values(config: Precheck.config,
                                         hide_keys: [:output_path],
                                             title: "Summary for precheck #{Fastlane::VERSION}")

      unless Spaceship::Tunes.client
        UI.message "Starting login with user '#{Precheck.config[:username]}'"
        Spaceship::Tunes.login(Precheck.config[:username])
        Spaceship::Tunes.select_team

        UI.message "Successfully logged in"
      end

      UI.message "Checking app for precheck rule violations"

      ensure_app_exists!

      processor_result = check_for_rule_violations(app: app, app_version: latest_app_version)

      if processor_result.items_not_checked?
        print_items_not_checked(processor_result: processor_result)
      end

      if processor_result.has_errors_or_warnings?
        summary_table = build_potential_problems_table(processor_result: processor_result)
        puts summary_table
      end

      if processor_result.should_trigger_user_error?
        UI.user_error!("precheck found one or more potential problems that must be addressed before submitting to review")
        return false
      end

      if !processor_result.has_errors_or_warnings? && !processor_result.items_not_checked?
        UI.message "precheck 👮‍♀️ 👮  finished without detecting any potential problems 🛫".green
      end

      return true
    end

    def print_items_not_checked(processor_result: nil)
      names = processor_result.items_not_checked.map(&:friendly_name)
      UI.message "😶  Metadata fields not checked by any rule: #{names.join(', ')}".yellow if names.length > 0
    end

    def build_potential_problems_table(processor_result: nil)
      error_results = processor_result.error_results
      warning_results = processor_result.warning_results

      rows = []

      warning_results.each do |rule, results|
        results.each do |result|
          rows << [result.item.friendly_name, result.rule_return.failure_data.yellow]
        end
      end

      error_results.each do |rule, results|
        results.each do |result|
          rows << [result.item.friendly_name, result.rule_return.failure_data.red]
        end
      end

      if rows.length == 0
        return nil
      else
        return Terminal::Table.new(
          title: "Potential problems".red,
          headings: ["Field", "Failure reason"],
          rows: FastlaneCore::PrintTable.transform_output(rows)
        ).to_s
      end
    end

    def check_for_rule_violations(app: nil, app_version: nil)
      Precheck::RuleProcessor.process_app_and_version(
        app: app,
        app_version: app_version,
        rules: Precheck::Options.rules
      )
    end

    def rendered_failed_results_table(failed_results: nil)
      rows = []
      failed_results.each do |failed_result|
        rows << [failed_result.rule.key, failed_result.item.friendly_name]
      end

      return Terminal::Table.new(
        title: "Failed rules".red,
        headings: ["Name", "App metadata field: (language code)"],
        rows: FastlaneCore::PrintTable.transform_output(rows)
      ).to_s
    end

    def rendered_rules_checked_table(rules_checked: nil)
      rows = []
      rules_checked.each do |rule|
        rows << [rule.key, rule.description]
      end

      return Terminal::Table.new(
        title: "Enabled rules".green,
        headings: ["Name", "Description"],
        rows: FastlaneCore::PrintTable.transform_output(rows)
      ).to_s
    end

    def rendered_skipped_rules_table(skipped_rules: nil)
      return nil if skipped_rules.empty?

      rows = []
      skipped_rules.each do |rule|
        rows << [rule.key, rule.description]
      end

      return Terminal::Table.new(
        title: "Skipped rules".yellow,
        headings: ["Name", "Description"],
        rows: FastlaneCore::PrintTable.transform_output(rows)
      ).to_s
    end

    def build_items_not_checked_table(items_not_checked: nil)
      rows = []
      items_not_checked.each do |item|
        rows << [item.item_name, item.friendly_name]
      end

      return Terminal::Table.new(
        title: "Not analyzed".yellow,
        headings: ["Name", "Friendly name"],
        rows: FastlaneCore::PrintTable.transform_output(rows)
      ).to_s
    end

    def app
      Spaceship::Tunes::Application.find(Precheck.config[:app_identifier])
    end

    def latest_app_version
      @latest_version ||= app.latest_version
    end

    # Makes sure the current App ID exists. If not, it will show an appropriate error message
    def ensure_app_exists!
      return if app
      UI.user_error!("Could not find app with App Identifier '#{Precheck.config[:app_identifier]}'")
    end
  end
end
